summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--locales/nb_NO.json556
-rw-r--r--src/invidious.cr185
-rw-r--r--src/invidious/channels.cr18
3 files changed, 481 insertions, 278 deletions
diff --git a/locales/nb_NO.json b/locales/nb_NO.json
index 753c1e9f..5dbd3e8f 100644
--- a/locales/nb_NO.json
+++ b/locales/nb_NO.json
@@ -1,280 +1,280 @@
{
- "`x` subscribers": "`x` abonnenter",
- "`x` videos": "`x` videoer",
- "LIVE": "SANNTIDSVISNING",
- "Shared `x` ago": "Delt for `x` siden",
- "Unsubscribe": "Opphev abonnement",
- "Subscribe": "Abonner",
- "Login to subscribe to `x`": "Logg inn for å abonnere på `x`",
- "View channel on YouTube": "Vis kanal på YouTube",
- "newest": "nyeste",
- "oldest": "eldste",
- "popular": "populært",
- "Preview page": "Forhåndsvis side",
- "Next page": "Neste side",
- "Clear watch history?": "Tøm visningshistorikk?",
- "Yes": "Ja",
- "No": "Nei",
- "Import and Export Data": "Importer- og eksporter data",
- "Import": "Importer",
- "Import Invidious data": "Importer Invidious-data",
- "Import YouTube subscriptions": "Importer YouTube-abonnenter",
- "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)",
- "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)",
- "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)",
- "Export": "Eksporter",
- "Export subscriptions as OPML": "Eksporter abonnenter som OPML",
- "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)",
- "Export data as JSON": "Eksporter data som JSON",
- "Delete account?": "Slett konto?",
- "History": "Historikk",
- "Previous page": "Forrige side",
- "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube",
- "JavaScript license information": "JavaScript-lisensinformasjon",
- "source": "kilde",
- "Login": "Logg inn",
- "Login/Register": "Logg inn/registrer",
- "Login to Google": "Logg inn med Google",
- "User ID:": "Bruker-ID:",
- "Password:": "Passord:",
- "Time (h:mm:ss):": "Tid (h:mm:ss):",
- "Text CAPTCHA": "Tekst-CAPTCHA",
- "Image CAPTCHA": "Bilde-CAPTCHA",
- "Sign In": "Innlogging",
- "Register": "Registrer",
- "Email:": "E-post:",
- "Google verification code:": "Google-bekreftelseskode:",
- "Preferences": "Innstillinger",
- "Player preferences": "Avspillerinnstillinger",
- "Always loop: ": "Alltid gjenta: ",
- "Autoplay: ": "Autoavspilling: ",
- "Autoplay next video: ": "Autospill neste video: ",
- "Listen by default: ": "Lytt som forvalg: ",
- "Default speed: ": "Forvalgt hastighet: ",
- "Preferred video quality: ": "Foretrukket videokvalitet: ",
- "Player volume: ": "Avspillerlydstyrke: ",
- "Default comments: ": "Forvalgte kommentarer: ",
- "Default captions: ": "Forvalgte undertitler: ",
- "Fallback captions: ": "Tilbakefallsundertitler: ",
- "Show related videos? ": "Vis relaterte videoer? ",
- "Visual preferences": "Visuelle innstillinger",
- "Dark mode: ": "Mørk drakt: ",
- "Thin mode: ": "Tynt modus: ",
- "Subscription preferences": "Abonnementsinnstillinger",
- "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ",
- "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ",
- "Sort videos by: ": "Sorter videoer etter: ",
- "published": "publisert",
- "published - reverse": "publisert - motsatt",
- "alphabetically": "alfabetisk",
- "alphabetically - reverse": "alfabetisk - motsatt",
- "channel name": "kanalnavn",
- "channel name - reverse": "kanalnavn - motsatt",
- "Only show latest video from channel: ": "Kun vis siste video fra kanal: ",
- "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ",
- "Only show unwatched: ": "Kun vis usette: ",
- "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ",
- "Data preferences": "Datainnstillinger",
- "Clear watch history": "Tøm visningshistorikk",
- "Import/Export data": "Importer/eksporter data",
- "Manage subscriptions": "Behandle abonnementer",
- "Watch history": "Visningshistorikk",
- "Delete account": "Slett konto",
- "Save preferences": "Lagre innstillinger",
- "Subscription manager": "Abonnementsbehandler",
- "`x` subscriptions": "`x` abonnementer",
- "Import/Export": "Importer/eksporter",
- "unsubscribe": "opphev abonnement",
- "Subscriptions": "Abonnement",
- "`x` unseen notifications": "`x` usette merknader",
- "search": "søk",
- "Sign out": "Logg ut",
- "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.",
- "Source available here.": "Kildekode tilgjengelig her.",
- "View JavaScript license information.": "Vis JavaScript-lisensinfo.",
- "Trending": "Trendsettende",
- "Watch video on Youtube": "Vis video på YouTube",
- "Genre: ": "Sjanger: ",
- "License: ": "Lisens: ",
- "Family friendly? ": "Familievennlig? ",
- "Wilson score: ": "Wilson-poengsum: ",
- "Engagement: ": "Engasjement: ",
- "Whitelisted regions: ": "Hvitlistede regioner: ",
- "Blacklisted regions: ": "Svartelistede regioner: ",
- "Shared `x`": "Delt `x`",
- "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.",
- "View YouTube comments": "Vis YouTube-kommentarer",
- "View more comments on Reddit": "Vis flere kommenterer på Reddit",
- "View `x` comments": "Vis `x` kommentarer",
- "View Reddit comments": "Vis Reddit-kommentarer",
- "Hide replies": "Skjul svar",
- "Show replies": "Vis svar",
- "Incorrect password": "Feil passord",
- "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer",
- "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.",
- "Invalid TFA code": "Ugyldig tofaktorkode",
- "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.",
- "Invalid answer": "Ugyldig svar",
- "Invalid CAPTCHA": "Ugyldig CAPTCHA",
- "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt",
- "User ID is a required field": "Bruker-ID er et påkrevd felt",
- "Password is a required field": "Passord er et påkrevd felt",
- "Invalid username or password": "Ugyldig brukernavn eller passord",
- "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"",
- "Password cannot be empty": "Passordet kan ikke være tomt",
- "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn",
- "Please sign in": "Logg inn",
- "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`",
- "channel:`x`": "kanal `x`",
- "Deleted or invalid channel": "Slettet eller ugyldig kanal",
- "This channel does not exist.": "Denne kanalen finnes ikke.",
- "Could not get channel info.": "Kunne ikke innhente kanalinfo.",
- "Could not fetch comments": "Kunne ikke hente kommentarer",
- "View `x` replies": "Vis `x` svar",
- "`x` ago": "`x` siden",
- "Load more": "Last inn flere",
- "`x` points": "`x` poeng",
- "Could not create mix.": "Kunne ikke opprette miks.",
- "Playlist is empty": "Spillelisten er tom",
- "Invalid playlist.": "Ugyldig spilleliste.",
- "Playlist does not exist.": "Spillelisten finnes ikke.",
- "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.",
- "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt",
- "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt",
- "Invalid challenge": "Ugyldig utfordring",
- "Invalid token": "Ugyldig symbol",
- "Invalid user": "Ugyldig bruker",
- "Token is expired, please try again": "Symbol utløpt, prøv igjen",
- "English": "Engelsk",
- "English (auto-generated)": "Engelsk (auto-generert)",
- "Afrikaans": "",
- "Albanian": "Albansk",
- "Amharic": "",
- "Arabic": "Arabisk",
- "Armenian": "Armensk",
- "Azerbaijani": "",
- "Bangla": "",
- "Basque": "",
- "Belarusian": "Hviterussisk",
- "Bosnian": "Bosnisk",
- "Bulgarian": "Bulgarsk",
- "Burmese": "Burmesisk",
- "Catalan": "Katalansk",
- "Cebuano": "",
- "Chinese (Simplified)": "",
- "Chinese (Traditional)": "",
- "Corsican": "",
- "Croatian": "",
- "Czech": "Tsjekkisk",
- "Danish": "Dansk",
- "Dutch": "",
- "Esperanto": "Esperanto",
- "Estonian": "",
- "Filipino": "",
- "Finnish": "Finsk",
- "French": "Fransk",
- "Galician": "",
- "Georgian": "",
- "German": "",
- "Greek": "",
- "Gujarati": "",
- "Haitian Creole": "",
- "Hausa": "",
- "Hawaiian": "",
- "Hebrew": "",
- "Hindi": "",
- "Hmong": "",
- "Hungarian": "Ungarsk",
- "Icelandic": "Islandsk",
- "Igbo": "",
- "Indonesian": "Indonesisk",
- "Irish": "Irsk",
- "Italian": "Italiensk",
- "Japanese": "Japansk",
- "Javanese": "",
- "Kannada": "",
- "Kazakh": "",
- "Khmer": "",
- "Korean": "",
- "Kurdish": "",
- "Kyrgyz": "",
- "Lao": "",
- "Latin": "",
- "Latvian": "",
- "Lithuanian": "",
- "Luxembourgish": "",
- "Macedonian": "",
- "Malagasy": "",
- "Malay": "",
- "Malayalam": "",
- "Maltese": "",
- "Maori": "",
- "Marathi": "",
- "Mongolian": "",
- "Nepali": "",
- "Norwegian": "Norsk bokmål",
- "Nyanja": "",
- "Pashto": "",
- "Persian": "",
- "Polish": "",
- "Portuguese": "",
- "Punjabi": "",
- "Romanian": "",
- "Russian": "Russisk",
- "Samoan": "",
- "Scottish Gaelic": "",
- "Serbian": "Serbisk",
- "Shona": "",
- "Sindhi": "",
- "Sinhala": "",
- "Slovak": "Slovakisk",
- "Slovenian": "Slovensk",
- "Somali": "Somali",
- "Southern Sotho": "",
- "Spanish": "Spansk",
- "Spanish (Latin America)": "",
- "Sundanese": "",
- "Swahili": "",
- "Swedish": "Svensk",
- "Tajik": "",
- "Tamil": "",
- "Telugu": "",
- "Thai": "",
- "Turkish": "Tyrkisk",
- "Ukrainian": "Ukrainsk",
- "Urdu": "",
- "Uzbek": "",
- "Vietnamese": "Vietnamesisk",
- "Welsh": "",
- "Western Frisian": "",
- "Xhosa": "",
- "Yiddish": "",
- "Yoruba": "",
- "Zulu": "",
- "`x` years": "`x` år",
- "`x` months": "`x` måneder",
- "`x` weeks": "`x` uker",
- "`x` days": "`x` dager",
- "`x` hours": "`x` timer",
- "`x` minutes": "`x` minutter",
- "`x` seconds": "`x` sekunder",
- "Fallback comments: ": "Tilbakefallskommentarer: ",
- "Popular": "Pupulært",
- "Top": "Topp",
- "About": "Om",
- "Rating: ": "Vurdering: ",
- "Language: ": "Språk: ",
- "Default": "",
- "Music": "",
- "Gaming": "",
- "News": "",
- "Movies": "",
- "Download": "",
- "Download as: ": "",
- "%A %B %-d, %Y": "",
- "(edited)": "",
- "Youtube permalink of the comment": "",
- "`x` marked it with a ❤": "",
- "Audio mode": "",
- "Video mode": ""
+ "`x` subscribers": "`x` abonnenter",
+ "`x` videos": "`x` videoer",
+ "LIVE": "SANNTIDSVISNING",
+ "Shared `x` ago": "Delt for `x` siden",
+ "Unsubscribe": "Opphev abonnement",
+ "Subscribe": "Abonner",
+ "Login to subscribe to `x`": "Logg inn for å abonnere på `x`",
+ "View channel on YouTube": "Vis kanal på YouTube",
+ "newest": "nyeste",
+ "oldest": "eldste",
+ "popular": "populært",
+ "Preview page": "Forhåndsvis side",
+ "Next page": "Neste side",
+ "Clear watch history?": "Tøm visningshistorikk?",
+ "Yes": "Ja",
+ "No": "Nei",
+ "Import and Export Data": "Importer- og eksporter data",
+ "Import": "Importer",
+ "Import Invidious data": "Importer Invidious-data",
+ "Import YouTube subscriptions": "Importer YouTube-abonnenter",
+ "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)",
+ "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)",
+ "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)",
+ "Export": "Eksporter",
+ "Export subscriptions as OPML": "Eksporter abonnenter som OPML",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)",
+ "Export data as JSON": "Eksporter data som JSON",
+ "Delete account?": "Slett konto?",
+ "History": "Historikk",
+ "Previous page": "Forrige side",
+ "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube",
+ "JavaScript license information": "JavaScript-lisensinformasjon",
+ "source": "kilde",
+ "Login": "Logg inn",
+ "Login/Register": "Logg inn/registrer",
+ "Login to Google": "Logg inn med Google",
+ "User ID:": "Bruker-ID:",
+ "Password:": "Passord:",
+ "Time (h:mm:ss):": "Tid (h:mm:ss):",
+ "Text CAPTCHA": "Tekst-CAPTCHA",
+ "Image CAPTCHA": "Bilde-CAPTCHA",
+ "Sign In": "Innlogging",
+ "Register": "Registrer",
+ "Email:": "E-post:",
+ "Google verification code:": "Google-bekreftelseskode:",
+ "Preferences": "Innstillinger",
+ "Player preferences": "Avspillerinnstillinger",
+ "Always loop: ": "Alltid gjenta: ",
+ "Autoplay: ": "Autoavspilling: ",
+ "Autoplay next video: ": "Autospill neste video: ",
+ "Listen by default: ": "Lytt som forvalg: ",
+ "Default speed: ": "Forvalgt hastighet: ",
+ "Preferred video quality: ": "Foretrukket videokvalitet: ",
+ "Player volume: ": "Avspillerlydstyrke: ",
+ "Default comments: ": "Forvalgte kommentarer: ",
+ "Default captions: ": "Forvalgte undertitler: ",
+ "Fallback captions: ": "Tilbakefallsundertitler: ",
+ "Show related videos? ": "Vis relaterte videoer? ",
+ "Visual preferences": "Visuelle innstillinger",
+ "Dark mode: ": "Mørk drakt: ",
+ "Thin mode: ": "Tynt modus: ",
+ "Subscription preferences": "Abonnementsinnstillinger",
+ "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ",
+ "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ",
+ "Sort videos by: ": "Sorter videoer etter: ",
+ "published": "publisert",
+ "published - reverse": "publisert - motsatt",
+ "alphabetically": "alfabetisk",
+ "alphabetically - reverse": "alfabetisk - motsatt",
+ "channel name": "kanalnavn",
+ "channel name - reverse": "kanalnavn - motsatt",
+ "Only show latest video from channel: ": "Kun vis siste video fra kanal: ",
+ "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ",
+ "Only show unwatched: ": "Kun vis usette: ",
+ "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ",
+ "Data preferences": "Datainnstillinger",
+ "Clear watch history": "Tøm visningshistorikk",
+ "Import/Export data": "Importer/eksporter data",
+ "Manage subscriptions": "Behandle abonnementer",
+ "Watch history": "Visningshistorikk",
+ "Delete account": "Slett konto",
+ "Save preferences": "Lagre innstillinger",
+ "Subscription manager": "Abonnementsbehandler",
+ "`x` subscriptions": "`x` abonnementer",
+ "Import/Export": "Importer/eksporter",
+ "unsubscribe": "opphev abonnement",
+ "Subscriptions": "Abonnement",
+ "`x` unseen notifications": "`x` usette merknader",
+ "search": "søk",
+ "Sign out": "Logg ut",
+ "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.",
+ "Source available here.": "Kildekode tilgjengelig her.",
+ "View JavaScript license information.": "Vis JavaScript-lisensinfo.",
+ "Trending": "Trendsettende",
+ "Watch video on Youtube": "Vis video på YouTube",
+ "Genre: ": "Sjanger: ",
+ "License: ": "Lisens: ",
+ "Family friendly? ": "Familievennlig? ",
+ "Wilson score: ": "Wilson-poengsum: ",
+ "Engagement: ": "Engasjement: ",
+ "Whitelisted regions: ": "Hvitlistede regioner: ",
+ "Blacklisted regions: ": "Svartelistede regioner: ",
+ "Shared `x`": "Delt `x`",
+ "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.",
+ "View YouTube comments": "Vis YouTube-kommentarer",
+ "View more comments on Reddit": "Vis flere kommenterer på Reddit",
+ "View `x` comments": "Vis `x` kommentarer",
+ "View Reddit comments": "Vis Reddit-kommentarer",
+ "Hide replies": "Skjul svar",
+ "Show replies": "Vis svar",
+ "Incorrect password": "Feil passord",
+ "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer",
+ "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.",
+ "Invalid TFA code": "Ugyldig tofaktorkode",
+ "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.",
+ "Invalid answer": "Ugyldig svar",
+ "Invalid CAPTCHA": "Ugyldig CAPTCHA",
+ "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt",
+ "User ID is a required field": "Bruker-ID er et påkrevd felt",
+ "Password is a required field": "Passord er et påkrevd felt",
+ "Invalid username or password": "Ugyldig brukernavn eller passord",
+ "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"",
+ "Password cannot be empty": "Passordet kan ikke være tomt",
+ "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn",
+ "Please sign in": "Logg inn",
+ "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`",
+ "channel:`x`": "kanal `x`",
+ "Deleted or invalid channel": "Slettet eller ugyldig kanal",
+ "This channel does not exist.": "Denne kanalen finnes ikke.",
+ "Could not get channel info.": "Kunne ikke innhente kanalinfo.",
+ "Could not fetch comments": "Kunne ikke hente kommentarer",
+ "View `x` replies": "Vis `x` svar",
+ "`x` ago": "`x` siden",
+ "Load more": "Last inn flere",
+ "`x` points": "`x` poeng",
+ "Could not create mix.": "Kunne ikke opprette miks.",
+ "Playlist is empty": "Spillelisten er tom",
+ "Invalid playlist.": "Ugyldig spilleliste.",
+ "Playlist does not exist.": "Spillelisten finnes ikke.",
+ "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.",
+ "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt",
+ "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt",
+ "Invalid challenge": "Ugyldig utfordring",
+ "Invalid token": "Ugyldig symbol",
+ "Invalid user": "Ugyldig bruker",
+ "Token is expired, please try again": "Symbol utløpt, prøv igjen",
+ "English": "Engelsk",
+ "English (auto-generated)": "Engelsk (auto-generert)",
+ "Afrikaans": "",
+ "Albanian": "Albansk",
+ "Amharic": "",
+ "Arabic": "Arabisk",
+ "Armenian": "Armensk",
+ "Azerbaijani": "",
+ "Bangla": "",
+ "Basque": "",
+ "Belarusian": "Hviterussisk",
+ "Bosnian": "Bosnisk",
+ "Bulgarian": "Bulgarsk",
+ "Burmese": "Burmesisk",
+ "Catalan": "Katalansk",
+ "Cebuano": "",
+ "Chinese (Simplified)": "",
+ "Chinese (Traditional)": "",
+ "Corsican": "",
+ "Croatian": "",
+ "Czech": "Tsjekkisk",
+ "Danish": "Dansk",
+ "Dutch": "",
+ "Esperanto": "Esperanto",
+ "Estonian": "",
+ "Filipino": "",
+ "Finnish": "Finsk",
+ "French": "Fransk",
+ "Galician": "",
+ "Georgian": "",
+ "German": "",
+ "Greek": "",
+ "Gujarati": "",
+ "Haitian Creole": "",
+ "Hausa": "",
+ "Hawaiian": "",
+ "Hebrew": "",
+ "Hindi": "",
+ "Hmong": "",
+ "Hungarian": "Ungarsk",
+ "Icelandic": "Islandsk",
+ "Igbo": "",
+ "Indonesian": "Indonesisk",
+ "Irish": "Irsk",
+ "Italian": "Italiensk",
+ "Japanese": "Japansk",
+ "Javanese": "",
+ "Kannada": "",
+ "Kazakh": "",
+ "Khmer": "",
+ "Korean": "",
+ "Kurdish": "",
+ "Kyrgyz": "",
+ "Lao": "",
+ "Latin": "",
+ "Latvian": "",
+ "Lithuanian": "",
+ "Luxembourgish": "",
+ "Macedonian": "",
+ "Malagasy": "",
+ "Malay": "",
+ "Malayalam": "",
+ "Maltese": "",
+ "Maori": "",
+ "Marathi": "",
+ "Mongolian": "",
+ "Nepali": "",
+ "Norwegian": "Norsk bokmål",
+ "Nyanja": "",
+ "Pashto": "",
+ "Persian": "",
+ "Polish": "",
+ "Portuguese": "",
+ "Punjabi": "",
+ "Romanian": "",
+ "Russian": "Russisk",
+ "Samoan": "",
+ "Scottish Gaelic": "",
+ "Serbian": "Serbisk",
+ "Shona": "",
+ "Sindhi": "",
+ "Sinhala": "",
+ "Slovak": "Slovakisk",
+ "Slovenian": "Slovensk",
+ "Somali": "Somali",
+ "Southern Sotho": "",
+ "Spanish": "Spansk",
+ "Spanish (Latin America)": "",
+ "Sundanese": "",
+ "Swahili": "",
+ "Swedish": "Svensk",
+ "Tajik": "",
+ "Tamil": "",
+ "Telugu": "",
+ "Thai": "",
+ "Turkish": "Tyrkisk",
+ "Ukrainian": "Ukrainsk",
+ "Urdu": "",
+ "Uzbek": "",
+ "Vietnamese": "Vietnamesisk",
+ "Welsh": "",
+ "Western Frisian": "",
+ "Xhosa": "",
+ "Yiddish": "",
+ "Yoruba": "",
+ "Zulu": "",
+ "`x` years": "`x` år",
+ "`x` months": "`x` måneder",
+ "`x` weeks": "`x` uker",
+ "`x` days": "`x` dager",
+ "`x` hours": "`x` timer",
+ "`x` minutes": "`x` minutter",
+ "`x` seconds": "`x` sekunder",
+ "Fallback comments: ": "Tilbakefallskommentarer: ",
+ "Popular": "Pupulært",
+ "Top": "Topp",
+ "About": "Om",
+ "Rating: ": "Vurdering: ",
+ "Language: ": "Språk: ",
+ "Default": "Forvalg",
+ "Music": "Musikk",
+ "Gaming": "Spill",
+ "News": "Nyheter",
+ "Movies": "Filmer",
+ "Download": "Last ned",
+ "Download as: ": "Last ned som: ",
+ "%A %B %-d, %Y": "",
+ "(edited)": "(redigert)",
+ "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet",
+ "`x` marked it with a ❤": "`x` levnet et ❤",
+ "Audio mode": "Lydmodus",
+ "Video mode": "Video-modus"
}
diff --git a/src/invidious.cr b/src/invidious.cr
index fa9fcacf..1ff94929 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -100,6 +100,7 @@ LOCALES = {
"de" => load_locale("de"),
"en-US" => load_locale("en-US"),
"fr" => load_locale("fr"),
+ "it" => load_locale("it"),
"nb_NO" => load_locale("nb_NO"),
"nl" => load_locale("nl"),
"pl" => load_locale("pl"),
@@ -1663,6 +1664,7 @@ post "/delete_account" do |env|
view_name = "subscriptions_#{sha256(user.email)[0..7]}"
PG_DB.exec("DROP MATERIALIZED VIEW #{view_name}")
PG_DB.exec("DELETE FROM users * WHERE email = $1", user.email)
+ PG_DB.exec("DELETE FROM session_ids * WHERE email = $1", user.email)
env.request.cookies.each do |cookie|
cookie.expires = Time.new(1990, 1, 1)
@@ -3129,6 +3131,189 @@ end
end
end
+["/api/v1/channels/:ucid/latest", "/api/v1/channels/latest/:ucid"].each do |route|
+ get route do |env|
+ env.response.content_type = "application/json"
+
+ ucid = env.params.url["ucid"]
+
+ begin
+ videos = get_latest_videos(ucid)
+ rescue ex
+ error_message = {"error" => ex.message}.to_json
+ halt env, status_code: 500, response: error_message
+ end
+
+ response = JSON.build do |json|
+ json.array do
+ videos.each do |video|
+ json.object do
+ json.field "title", video.title
+ json.field "videoId", video.id
+
+ json.field "authorId", ucid
+ json.field "authorUrl", "/channel/#{ucid}"
+
+ json.field "videoThumbnails" do
+ generate_thumbnails(json, video.id)
+ end
+
+ json.field "description", video.description
+ json.field "descriptionHtml", video.description_html
+
+ json.field "viewCount", video.views
+ json.field "published", video.published.to_unix
+ json.field "publishedText", "#{recode_date(video.published)} ago"
+ json.field "lengthSeconds", video.length_seconds
+ json.field "liveNow", video.live_now
+ json.field "paid", video.paid
+ json.field "premium", video.premium
+ end
+ end
+ end
+ end
+
+ if env.params.query["pretty"]? && env.params.query["pretty"] == "1"
+ JSON.parse(response).to_pretty_json
+ else
+ response
+ end
+ end
+end
+
+["/api/v1/channels/:ucid/playlists", "/api/v1/channels/playlists/:ucid"].each do |route|
+ get route do |env|
+ locale = LOCALES[env.get("locale").as(String)]?
+
+ env.response.content_type = "application/json"
+
+ ucid = env.params.url["ucid"]
+ continuation = env.params.query["continuation"]?
+ sort_by = env.params.query["sort"]?.try &.downcase
+ sort_by ||= env.params.query["sort_by"]?.try &.downcase
+ sort_by ||= "last"
+
+ begin
+ author, ucid, auto_generated = get_about_info(ucid, locale)
+ rescue ex
+ error_message = ex.message
+ halt env, status_code: 500, response: error_message
+ end
+
+ client = make_client(YT_URL)
+
+ if continuation
+ url = produce_channel_playlists_url(ucid, continuation, sort_by, auto_generated)
+
+ response = client.get(url)
+ json = JSON.parse(response.body)
+
+ if json["load_more_widget_html"].as_s.empty?
+ response = {
+ "playlists" => [] of String,
+ "continuation" => nil,
+ }
+
+ if env.params.query["pretty"]? && env.params.query["pretty"] == "1"
+ response = response.to_pretty_json
+ else
+ response = response.to_json
+ end
+
+ halt env, status_code: 200, response: response
+ end
+
+ continuation = XML.parse_html(json["load_more_widget_html"].as_s)
+ continuation = continuation.xpath_node(%q(//button[@data-uix-load-more-href]))
+ if continuation
+ continuation = extract_channel_playlists_cursor(continuation["data-uix-load-more-href"], auto_generated)
+ end
+
+ html = XML.parse_html(json["content_html"].as_s)
+ nodeset = html.xpath_nodes(%q(//li[contains(@class, "feed-item-container")]))
+ else
+ url = "/channel/#{ucid}/playlists?disable_polymer=1&flow=list"
+
+ if auto_generated
+ url += "&view=50"
+ else
+ url += "&view=1"
+ end
+
+ case sort_by
+ when "last", "last_added"
+ #
+ when "oldest", "oldest_created"
+ url += "&sort=da"
+ when "newest", "newest_created"
+ url += "&sort=dd"
+ end
+
+ response = client.get(url)
+ html = XML.parse_html(response.body)
+
+ continuation = html.xpath_node(%q(//button[@data-uix-load-more-href]))
+ if continuation
+ continuation = extract_channel_playlists_cursor(continuation["data-uix-load-more-href"], auto_generated)
+ end
+
+ nodeset = html.xpath_nodes(%q(//ul[@id="browse-items-primary"]/li[contains(@class, "feed-item-container")]))
+ end
+
+ if auto_generated
+ items = extract_shelf_items(nodeset, ucid, author)
+ else
+ items = extract_items(nodeset, ucid, author)
+ end
+
+ response = JSON.build do |json|
+ json.object do
+ json.field "playlists" do
+ json.array do
+ items.each do |item|
+ json.object do
+ if item.is_a?(SearchPlaylist)
+ json.field "title", item.title
+ json.field "playlistId", item.id
+
+ json.field "author", item.author
+ json.field "authorId", item.ucid
+ json.field "authorUrl", "/channel/#{item.ucid}"
+
+ json.field "videoCount", item.video_count
+ json.field "videos" do
+ json.array do
+ item.videos.each do |video|
+ json.object do
+ json.field "title", video.title
+ json.field "videoId", video.id
+ json.field "lengthSeconds", video.length_seconds
+
+ json.field "videoThumbnails" do
+ generate_thumbnails(json, video.id)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ json.field "continuation", continuation
+ end
+ end
+
+ if env.params.query["pretty"]? && env.params.query["pretty"] == "1"
+ JSON.parse(response).to_pretty_json
+ else
+ response
+ end
+ end
+end
+
get "/api/v1/channels/search/:ucid" do |env|
locale = LOCALES[env.get("locale").as(String)]?
diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr
index b6692919..d852e517 100644
--- a/src/invidious/channels.cr
+++ b/src/invidious/channels.cr
@@ -460,3 +460,21 @@ def get_60_videos(ucid, page, auto_generated, sort_by = "newest")
return videos, count
end
+
+def get_latest_videos(ucid)
+ client = make_client(YT_URL)
+ videos = [] of SearchVideo
+
+ url = produce_channel_videos_url(ucid, 0)
+ response = client.get(url)
+ json = JSON.parse(response.body)
+
+ if json["content_html"]? && !json["content_html"].as_s.empty?
+ document = XML.parse_html(json["content_html"].as_s)
+ nodeset = document.xpath_nodes(%q(//li[contains(@class, "feed-item-container")]))
+
+ videos = extract_videos(nodeset, ucid)
+ end
+
+ return videos
+end