diff options
31 files changed, 661 insertions, 166 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c0485266 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a bug report to help us improve Invidious +title: '[Bug] ' +labels: bug +assignees: '' + +--- + +<!-- Please use the search function to check if the bug you found has already been reported by someone else --> +<!-- If you want to suggest a new feature please use "Feature request" instead --> +<!-- If you want to suggest an enhancement to an existing feature please use "Enhancement" instead --> + +**Describe the bug** +<!-- A clear and concise description of what the bug is. --> + +**Steps to Reproduce** +<!-- Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error +--> + +**Logs** +<!-- If applicable, copy the log that appear in the browser page where the error is reported. --> + +**Screenshots** +<!-- If applicable, add screenshots to help explain your problem. --> + +**Additional context** +<!-- Add any other context about the problem here. + - Browser (if applicable): + - OS (if applicable): +--> diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md new file mode 100644 index 00000000..7823e1df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -0,0 +1,24 @@ +--- +name: Enhancement +about: Suggest an enhancement for an existing feature +title: '[Enhancement] ' +labels: enhancement +assignees: '' + +--- + +<!-- Please use the search function to check if the desired function has already been requested by someone else --> + <!-- If you want to suggest a new feature please use "Feature request" instead --> +<!-- If you want to report a bug, please use "Bug report" instead --> + +**Is your enhancement request related to a problem? Please describe.** +<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] --> + +**Describe the solution you'd like** +<!-- A clear and concise description of what you want to happen. --> + +**Describe alternatives you've considered** +<!-- A clear and concise description of any alternative solutions or features you've considered. --> + +**Additional context** +<!-- Add any other context or screenshots about the enhancement here. --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..59692a51 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '[Feature request] ' +labels: feature-request +assignees: '' + +--- + +<!-- Please use the search function to check if the desired function has already been requested by someone else --> +<!-- If you want to suggest an enhancement to an existing feature please use "Enhancement" instead --> +<!-- If you want to report a bug, please use "Bug report" instead --> + +**Is your feature request related to a problem? Please describe.** +<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] --> + +**Describe the solution you'd like** +<!-- A clear and concise description of what you want to happen. --> + +**Describe alternatives you've considered** +<!-- A clear and concise description of any alternative solutions or features you've considered. --> + +**Additional context** +<!-- Add any other context or screenshots about the feature request here. --> diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc0e8096..66aacff9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: Install Crystal uses: oprypin/install-crystal@v1.2.4 with: - crystal: 0.35.1 + crystal: 0.36.1 - name: Cache Shards uses: actions/cache@v2 @@ -91,10 +91,8 @@ $ sudo pacman -S base-devel shards crystal librsvg postgresql # Ubuntu or Debian # First you have to add the repository to your APT configuration. For easy setup just run in your command line: -$ curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash -# That will add the signing key and the repository configuration. If you prefer to do it manually, execute the following commands: -$ curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add - -$ echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list +$ curl -fsSL https://crystal-lang.org/install.sh | sudo bash +# That will add the signing key and the repository configuration. If you prefer to do it manually, Follow the instructions here https://crystal-lang.org/install $ sudo apt-get update $ sudo apt install crystal libssl-dev libxml2-dev libyaml-dev libgmp-dev libreadline-dev postgresql librsvg2-bin libsqlite3-dev zlib1g-dev ``` diff --git a/docker/Dockerfile b/docker/Dockerfile index ce4cc765..b88a76c0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM crystallang/crystal:0.35.1-alpine AS builder +FROM crystallang/crystal:0.36.1-alpine AS builder RUN apk add --no-cache curl sqlite-static WORKDIR /invidious COPY ./shard.yml ./shard.yml diff --git a/locales/fi.json b/locales/fi.json index 0b2db9a1..5dfd4ea2 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -1,10 +1,10 @@ { "`x` subscribers.([^.,0-9]|^)1([^.,0-9]|$)": "`x` tilaaja", - "`x` subscribers.": "`x` tilaajaa", + "`x` subscribers.": "`x` tilaajaa.", "`x` videos.([^.,0-9]|^)1([^.,0-9]|$)": "`x` video", - "`x` videos.": "`x` videota", + "`x` videos.": "`x` videota.", "`x` playlists.([^.,0-9]|^)1([^.,0-9]|$)": "`x` soittolista.([^.,0-9]|^)1([^.,0-9]|$)", - "`x` playlists.": "`x` soittolistaa", + "`x` playlists.": "`x` soittolistaa.", "LIVE": "SUORA", "Shared `x` ago": "Jaettu `x` sitten", "Unsubscribe": "Peruuta tilaus", @@ -41,7 +41,7 @@ "An alternative front-end to YouTube": "Vaihtoehtoinen käyttöliittymä YouTubelle", "JavaScript license information": "JavaScript-käyttöoikeustiedot", "source": "lähde", - "Log in": "Kirjaudu sisään", + "Log in": "Kirjaudu", "Log in/register": "Kirjaudu sisään / Rekisteröidy", "Log in with Google": "Kirjaudu sisään Googlella", "User ID": "Käyttäjätunnus", diff --git a/locales/he.json b/locales/he.json new file mode 100644 index 00000000..f5e33877 --- /dev/null +++ b/locales/he.json @@ -0,0 +1,414 @@ +{ + "`x` subscribers": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` רשומים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` רשומים." + }, + "`x` videos": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` סרטונים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` סרטונים." + }, + "`x` playlists": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` פלייליסטים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` פלייליסטים." + }, + "LIVE": "שידור חי", + "Shared `x` ago": "", + "Unsubscribe": "ביטול מינוי", + "Subscribe": "הרשמה למינוי", + "View channel on YouTube": "צפייה בערוץ ב־YouTube", + "View playlist on YouTube": "צפייה בפלייליסט ב־YouTube", + "newest": "החדש ביותר", + "oldest": "הישן ביותר", + "popular": "פופולארי", + "last": "אחרון", + "Next page": "העמוד הבא", + "Previous page": "העמוד הקודם", + "Clear watch history?": "לנקות את היסטוריית הצפייה?", + "New password": "סיסמה חדשה", + "New passwords must match": "על הסיסמאות החדשות להתאים", + "Cannot change password for Google accounts": "לא ניתן לשנות את הסיסמה לחשבונות Google", + "Authorize token?": "", + "Authorize token for `x`?": "", + "Yes": "כן", + "No": "לא", + "Import and Export Data": "ייבוא וייצוא נתונים", + "Import": "ייבוא", + "Import Invidious data": "ייבוא נתוני Invidious", + "Import YouTube subscriptions": "ייבוא מינויים מ־YouTube", + "Import FreeTube subscriptions (.db)": "ייבוא מינויים מ־FreeTube (.db)", + "Import NewPipe subscriptions (.json)": "ייבוא מינויים מ־NewPipe (.json)", + "Import NewPipe data (.zip)": "ייבוא נתוני NewPipe (.zip)", + "Export": "ייצוא", + "Export subscriptions as OPML": "ייצוא המינויים בתור OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "ייצוא המינויים בתור OPML (עבור NewPipe ו־FreeTube)", + "Export data as JSON": "ייצוא הנתונים בתור קובץ JSON", + "Delete account?": "למחוק את החשבון?", + "History": "היסטוריה", + "An alternative front-end to YouTube": "ממשק משתמש חלופי ל־YouTube", + "JavaScript license information": "", + "source": "source", + "Log in": "כניסה", + "Log in/register": "כניסה/הרשמה", + "Log in with Google": "כניסה עם Google", + "User ID": "שם משתמש", + "Password": "סיסמה", + "Time (h:mm:ss):": "זמן (h:mm:ss):", + "Text CAPTCHA": "Text CAPTCHA", + "Image CAPTCHA": "Image CAPTCHA", + "Sign In": "התחברות", + "Register": "הרשמה", + "E-mail": "דוא״ל", + "Google verification code": "קוד האימות של Google", + "Preferences": "העדפות", + "Player preferences": "העדפות הנגן", + "Always loop: ": "", + "Autoplay: ": "ניגון אוטומטי: ", + "Play next by default: ": "", + "Autoplay next video: ": "נגן אוטומטית את הסרטון הבא ", + "Listen by default: ": "האזן כברירת מחדל: ", + "Proxy videos: ": "", + "Default speed: ": "מהירות ברירת המחדל: ", + "Preferred video quality: ": "איכות הווידאו המועדפת: ", + "Player volume: ": "עוצמת שמע בנגן: ", + "Default comments: ": "תגובות ברירת מחדל ", + "youtube": "יוטיוב", + "reddit": "reddit", + "Default captions: ": "כתוביות ברירת מחדל ", + "Fallback captions: ": "כתוביות גיבוי ", + "Show related videos: ": "הראה סרטונים קשורים: ", + "Show annotations by default: ": "הראה הסברים כברירת מחדל: ", + "Visual preferences": "העדפות חזותיות", + "Player style: ": "סגנון הנגן: ", + "Dark mode: ": "מצב כהה: ", + "Theme: ": "ערכת נושא: ", + "dark": "כהה", + "light": "בהיר", + "Thin mode: ": "", + "Subscription preferences": "העדפות מינויים", + "Show annotations by default for subscribed channels: ": "Show annotations by default for subscribed channels? ", + "Redirect homepage to feed: ": "", + "Number of videos shown in feed: ": "מספר הסרטונים שמוצגים בהזנה: ", + "Sort videos by: ": "מיון הסרטונים לפי: ", + "published": "פורסם", + "published - reverse": "", + "alphabetically": "בסדר אלפביתי", + "alphabetically - reverse": "בסדר אלפביתי - הפוך", + "channel name": "שם הערוץ", + "channel name - reverse": "שם הערוץ - הפוך", + "Only show latest video from channel: ": "הראה רק את הסרטון האחרון מהערוץ: ", + "Only show latest unwatched video from channel: ": "הראה רק את הסרטון האחרון שלא נצפה מהערוץ: ", + "Only show unwatched: ": "הראה רק סרטונים שלא נצפו ", + "Only show notifications (if there are any): ": "הראה רק התראות (אם יש) ", + "Enable web notifications": "", + "`x` uploaded a video": "סרטון הועלה על ידי `x`", + "`x` is live": "`x` בשידור חי", + "Data preferences": "העדפות נתונים", + "Clear watch history": "ניקוי היסטוריית הצפייה", + "Import/export data": "ייבוא/ייצוא נתונים", + "Change password": "שינוי הסיסמה", + "Manage subscriptions": "ניהול מינויים", + "Manage tokens": "", + "Watch history": "היסטוריית צפייה", + "Delete account": "מחיקת החשבון", + "Administrator preferences": "", + "Default homepage: ": "Default homepage: ", + "Feed menu: ": "תפריט ההזנה: ", + "Top enabled: ": "", + "CAPTCHA enabled: ": "", + "Login enabled: ": "", + "Registration enabled: ": "", + "Report statistics: ": "", + "Save preferences": "שמירת ההעדפות", + "Subscription manager": "מנהל המינויים", + "Token manager": "Token manager", + "Token": "Token", + "`x` subscriptions": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` מינויים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` מינויים." + }, + "`x` tokens": { + "([^.,0-9]|^)1([^.,0-9]|$)": "", + "": "" + }, + "Import/export": "ייבוא/ייצוא", + "unsubscribe": "ביטול מנוי", + "revoke": "", + "Subscriptions": "מינויים", + "`x` unseen notifications": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` הודעות שלא נראו.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` הודעות שלא נראו." + }, + "search": "חיפוש", + "Log out": "יציאה", + "Released under the AGPLv3 by Omar Roth.": "מופץ תחת רישיון AGPLv3 על ידי עמר רות׳ (Omar Roth).", + "Source available here.": "קוד המקור זמין כאן.", + "View JavaScript license information.": "", + "View privacy policy.": "להצגת מדיניות הפרטיות.", + "Trending": "הסרטונים החמים", + "Public": "ציבורי", + "Unlisted": "לא רשום", + "Private": "פרטי", + "View all playlists": "הצגת כל הפלייליסטים", + "Updated `x` ago": "הועלה לפני `x`", + "Delete playlist `x`?": "למחוק את פלייליסט `x`?", + "Delete playlist": "מחיקת פלייליסט", + "Create playlist": "יצירת פלייליסט", + "Title": "", + "Playlist privacy": "Playlist privacy", + "Editing playlist `x`": "", + "Watch on YouTube": "צפייה ב־YouTube", + "Hide annotations": "", + "Show annotations": "", + "Genre: ": "Genre: ", + "License: ": "רישיון: ", + "Family friendly? ": "לכל המשפחה? ", + "Wilson score: ": "", + "Engagement: ": "", + "Whitelisted regions: ": "", + "Blacklisted regions: ": "", + "Shared `x`": "", + "`x` views": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` צפיות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` צפיות." + }, + "Premieres in `x`": "", + "Premieres `x`": "", + "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "שלום! נראה ש־JavaScript כבוי. יש ללחוץ כאן להצגת התגובות, נא לקחת בחשבון שהטעינה תיקח קצת יותר זמן.", + "View YouTube comments": "", + "View more comments on Reddit": "", + "View `x` comments": { + "([^.,0-9]|^)1([^.,0-9]|$)": "הצגת `x` תגובות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "הצגת `x` תגובות." + }, + "View Reddit comments": "", + "Hide replies": "הסתרת תגובות", + "Show replies": "הצגת תגובות", + "Incorrect password": "סיסמה שגויה", + "Quota exceeded, try again in a few hours": "", + "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "", + "Invalid TFA code": "", + "Login failed. This may be because two-factor authentication is not turned on for your account.": "", + "Wrong answer": "תשובה שגויה", + "Erroneous CAPTCHA": "", + "CAPTCHA is a required field": "", + "User ID is a required field": "חובה למלא את שדה שם המשתמש", + "Password is a required field": "חובה למלא את שדה הסיסמה", + "Wrong username or password": "שם משתמש שגוי או סיסמה שגויה", + "Please sign in using 'Log in with Google'": "", + "Password cannot be empty": "", + "Password cannot be longer than 55 characters": "", + "Please log in": "נא להתחבר", + "Invidious Private Feed for `x`": "", + "channel:`x`": "ערוץ:`x`", + "Deleted or invalid channel": "", + "This channel does not exist.": "הערוץ הזה אינו קיים.", + "Could not get channel info.": "לא היה ניתן לקבל מידע על הערוץ.", + "Could not fetch comments": "לא היה ניתן למשוך את התגובות", + "View `x` replies": { + "([^.,0-9]|^)1([^.,0-9]|$)": "הצגת `x` תגובות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "הצגת `x` תגובות." + }, + "`x` ago": "לפני `x`", + "Load more": "לטעון עוד", + "`x` points": { + "([^.,0-9]|^)1([^.,0-9]|$)": "", + "": "" + }, + "Could not create mix.": "", + "Empty playlist": "פלייליסט ריק", + "Not a playlist.": "לא פלייליסט.", + "Playlist does not exist.": "הפלייליסט אינו קיים.", + "Could not pull trending pages.": "", + "Hidden field \"challenge\" is a required field": "", + "Hidden field \"token\" is a required field": "", + "Erroneous challenge": "", + "Erroneous token": "", + "No such user": "אין משתמש כזה", + "Token is expired, please try again": "", + "English": "אנגלית", + "English (auto-generated)": "אנגלית (נוצר באופן אוטומטי)", + "Afrikaans": "Afrikaans", + "Albanian": "אלבנית", + "Amharic": "אמהרית", + "Arabic": "ערבית", + "Armenian": "ארמנית", + "Azerbaijani": "Azerbaijani", + "Bangla": "בנגלית", + "Basque": "בסקית", + "Belarusian": "Belarusian", + "Bosnian": "Bosnian", + "Bulgarian": "בולגרית", + "Burmese": "Burmese", + "Catalan": "Catalan", + "Cebuano": "סבואנו", + "Chinese (Simplified)": "סינית (מפושטת)", + "Chinese (Traditional)": "סינית (מסורתית)", + "Corsican": "קורסיקאית", + "Croatian": "קרואטית", + "Czech": "צ׳כית", + "Danish": "Danish", + "Dutch": "Dutch", + "Esperanto": "אספרנטו", + "Estonian": "אסטונית", + "Filipino": "Filipino", + "Finnish": "Finnish", + "French": "צרפתית", + "Galician": "גליסית", + "Georgian": "גאורגית", + "German": "גרמנית", + "Greek": "יוונית", + "Gujarati": "גוג׳ראטית", + "Haitian Creole": "קריאולית האיטית", + "Hausa": "האוסה", + "Hawaiian": "הוואית", + "Hebrew": "עברית", + "Hindi": "הינדית", + "Hmong": "", + "Hungarian": "הונגרית", + "Icelandic": "איסלנדית", + "Igbo": "", + "Indonesian": "אינדונזית", + "Irish": "Irish", + "Italian": "איטלקית", + "Japanese": "יפנית", + "Javanese": "ג'אווה", + "Kannada": "קאנדה", + "Kazakh": "קזחית", + "Khmer": "חמרית", + "Korean": "קוריאנית", + "Kurdish": "כורדית", + "Kyrgyz": "קירגיזית", + "Lao": "לאית", + "Latin": "לטינית", + "Latvian": "לטבית", + "Lithuanian": "ליטאית", + "Luxembourgish": "לוקסמבורגית", + "Macedonian": "מקדונית", + "Malagasy": "מלגשית", + "Malay": "מלאית", + "Malayalam": "", + "Maltese": "מלטזית", + "Maori": "מאורית", + "Marathi": "מראטהית", + "Mongolian": "מונגולית", + "Nepali": "נפאלית", + "Norwegian Bokmål": "Norwegian Bokmål", + "Nyanja": "", + "Pashto": "פשטו", + "Persian": "פרסית", + "Polish": "פולנית", + "Portuguese": "פורטוגלית", + "Punjabi": "פנג'אבי", + "Romanian": "רומנית", + "Russian": "רוסית", + "Samoan": "", + "Scottish Gaelic": "גאלית סקוטית", + "Serbian": "Serbian", + "Shona": "", + "Sindhi": "סינדהי", + "Sinhala": "סינהלית", + "Slovak": "Slovak", + "Slovenian": "Slovenian", + "Somali": "סומלית", + "Southern Sotho": "", + "Spanish": "ספרדית", + "Spanish (Latin America)": "ספרדית (אמריקה הלטינית)", + "Sundanese": "", + "Swahili": "סווהילי", + "Swedish": "שוודית", + "Tajik": "טג׳יקית", + "Tamil": "טמילית", + "Telugu": "טלוגו", + "Thai": "תאית", + "Turkish": "טורקית", + "Ukrainian": "אוקראינית", + "Urdu": "אורדו", + "Uzbek": "אוזבקית", + "Vietnamese": "וייטנאמית", + "Welsh": "ולשית", + "Western Frisian": "", + "Xhosa": "קוסה", + "Yiddish": "יידיש", + "Yoruba": "", + "Zulu": "זולו", + "`x` years": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` שנים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` שנים." + }, + "`x` months": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` חודשים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` חודשים." + }, + "`x` weeks": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` שבועות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` שבועות." + }, + "`x` days": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` ימים.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` ימים." + }, + "`x` hours": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` שעות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` שעות." + }, + "`x` minutes": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` דקות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` דקות." + }, + "`x` seconds": { + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` שניות.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` שניות." + }, + "Fallback comments: ": "", + "Popular": "", + "Top": "Top", + "About": "על אודות", + "Rating: ": "דירוג: ", + "Language: ": "שפה: ", + "View as playlist": "הצגה כפלייליסט", + "Default": "ברירת מחדל", + "Music": "מוזיקה", + "Gaming": "משחקים", + "News": "חדשות", + "Movies": "סרטים", + "Download": "הורדה", + "Download as: ": "הורדה בתור: ", + "%A %B %-d, %Y": "%A %B %-d, %Y", + "(edited)": "", + "YouTube comment permalink": "", + "permalink": "", + "`x` marked it with a ❤": "סומנה ב־❤ על ידי `x`", + "Audio mode": "Audio mode", + "Video mode": "Video mode", + "Videos": "סרטונים", + "Playlists": "פלייליסטים", + "Community": "קהילה", + "relevance": "רלוונטיות", + "rating": "דירוג", + "date": "תאריך העלאה", + "views": "מספר צפיות", + "content_type": "סוג", + "duration": "משך זמן", + "features": "תכונות", + "sort": "מיון לפי", + "hour": "השעה האחרונה", + "today": "היום", + "week": "השבוע", + "month": "החודש", + "year": "השנה", + "video": "סרטון", + "channel": "ערוץ", + "playlist": "פלייליסט", + "movie": "סרט", + "show": "תכנית טלוויזיה", + "hd": "HD", + "subtitles": "כתוביות", + "creative_commons": "Creative Commons", + "3d": "3D", + "live": "Live", + "4k": "4K", + "location": "מיקום", + "hdr": "HDR", + "filter": "סינון", + "Current version: ": "הגרסה הנוכחית: " +} diff --git a/locales/id.json b/locales/id.json index 62577127..66662c69 100644 --- a/locales/id.json +++ b/locales/id.json @@ -74,7 +74,7 @@ "youtube": "youtube", "reddit": "reddit", "Default captions: ": "Subtitel default: ", - "Fallback captions: ": "", + "Fallback captions: ": "Subtitel fallback: ", "Show related videos: ": "Tampilkan video terkait: ", "Show annotations by default: ": "Tampilkan anotasi secara default: ", "Visual preferences": "Preferensi visual", @@ -163,7 +163,7 @@ "License: ": "Lisensi: ", "Family friendly? ": "Ramah keluarga? ", "Wilson score: ": "Skor Wilson: ", - "Engagement: ": "Keterikatan: ", + "Engagement: ": "Keterlibatan: ", "Whitelisted regions: ": "Wilayah daftar-putih: ", "Blacklisted regions: ": "Wilayah daftar-hitam: ", "Shared `x`": "Berbagi`x`", @@ -211,10 +211,10 @@ "`x` ago": "`x` lalu", "Load more": "Muat lebih banyak", "`x` points": { - "([^.,0-9]|^)1([^.,0-9]|$)": "", - "": "" + "([^.,0-9]|^)1([^.,0-9]|$)": "`x` titik.([^.,0-9]|^)1([^.,0-9]|$)", + "": "`x` titik." }, - "Could not create mix.": "", + "Could not create mix.": "Tidak dapat membuat mix.", "Empty playlist": "Daftar putar kosong", "Not a playlist.": "Bukan daftar putar.", "Playlist does not exist.": "Daftar putar tidak ada.", @@ -232,100 +232,100 @@ "Amharic": "Bahasa Amharik", "Arabic": "Bahasa arab", "Armenian": "Bahasa Armenia", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "", + "Azerbaijani": "Bahasa Azeri", + "Bangla": "Bahasa Bangla", + "Basque": "Bahasa Basque", + "Belarusian": "Bahasa Belarusia", "Bosnian": "Bahasa Bosnia", "Bulgarian": "Bahasa Bulgaria", "Burmese": "Bahasa Birma", - "Catalan": "", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", + "Catalan": "Bahasa Catalan", + "Cebuano": "Bahasa Cebu", + "Chinese (Simplified)": "Bahasa Cina", + "Chinese (Traditional)": "Bahasa Cina (Tradisonal)", "Corsican": "", "Croatian": "Bahasa Kroasia", "Czech": "Bahasa Ceko", - "Danish": "", + "Danish": "Bahasa Denmak", "Dutch": "Bahasa Belanda", - "Esperanto": "", - "Estonian": "", - "Filipino": "", - "Finnish": "", - "French": "", + "Esperanto": "Bahasa Esperanto", + "Estonian": "Bahasa Estonia", + "Filipino": "Bahasa Filipina", + "Finnish": "Bahasa Finlandia", + "French": "Bahasa Perancis", "Galician": "", - "Georgian": "", - "German": "", + "Georgian": "Bahasa Georgia", + "German": "Bahasa Jerman", "Greek": "Bahasa Yunani", "Gujarati": "", "Haitian Creole": "", "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", + "Hawaiian": "Bahasa Hawai", + "Hebrew": "Bahasa Ibrani", + "Hindi": "Bahasa Hindi", "Hmong": "", - "Hungarian": "", - "Icelandic": "", - "Igbo": "", + "Hungarian": "Bahasa Hungaria", + "Icelandic": "Bahasa Islandia", + "Igbo": "Bahasa Igbo", "Indonesian": "Bahasa Indonesia", - "Irish": "", - "Italian": "", + "Irish": "Bahasa Irlandia", + "Italian": "Bahasa Italia", "Japanese": "Bahasa Jepang", "Javanese": "Bahasa Jawa", "Kannada": "", "Kazakh": "", "Khmer": "", "Korean": "Bahasa Korea", - "Kurdish": "", + "Kurdish": "Bahasa Kurdistan", "Kyrgyz": "", "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", + "Latin": "Bahasa Latin", + "Latvian": "Bahasa Latvia", + "Lithuanian": "Bahasa Lithuania", "Luxembourgish": "", "Macedonian": "", "Malagasy": "", "Malay": "Bahasa Melayu", "Malayalam": "", "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", + "Maori": "Bahasa Maori", + "Marathi": "Bahasa Marathi", + "Mongolian": "Bahasa Mongolia", + "Nepali": "Bahasa Nepal", "Norwegian Bokmål": "", "Nyanja": "", "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "", + "Persian": "Bahasa Persia", + "Polish": "Bahasa Polandia", + "Portuguese": "Bahasa Portugis", + "Punjabi": "Bahasa Punjabi", + "Romanian": "Bahasa Romania", + "Russian": "Bahasa Russia", "Samoan": "", "Scottish Gaelic": "", - "Serbian": "", + "Serbian": "Bahasa Serbia", "Shona": "", "Sindhi": "", "Sinhala": "", - "Slovak": "", - "Slovenian": "", - "Somali": "", + "Slovak": "Bahasa Slovakia", + "Slovenian": "Bahasa Slovenia", + "Somali": "Bahasa Somalia", "Southern Sotho": "", - "Spanish": "", - "Spanish (Latin America)": "", + "Spanish": "Bahasa Spanyol", + "Spanish (Latin America)": "Bahasa Spanyol (Amerika Latin)", "Sundanese": "Bahasa Sunda", - "Swahili": "", - "Swedish": "", + "Swahili": "Bahasa Swahili", + "Swedish": "Bahasa Swedia", "Tajik": "", - "Tamil": "", + "Tamil": "Bahasa Tamil", "Telugu": "", "Thai": "Bahasa Thailand", - "Turkish": "", - "Ukrainian": "", - "Urdu": "", - "Uzbek": "", + "Turkish": "Bahasa Turki", + "Ukrainian": "Bahasa Ukraina", + "Urdu": "Bahasa Urdu", + "Uzbek": "Bahasa Uzbek", "Vietnamese": "Bahasa Vietnam", - "Welsh": "", + "Welsh": "Bahasa Wales", "Western Frisian": "", "Xhosa": "", "Yiddish": "", @@ -376,7 +376,7 @@ "%A %B %-d, %Y": "", "(edited)": "(disunting)", "YouTube comment permalink": "", - "permalink": "", + "permalink": "permalink", "`x` marked it with a ❤": "`x` telah ditandai dengan ❤", "Audio mode": "Mode audio", "Video mode": "Mode video", @@ -22,7 +22,7 @@ shards: pg: git: https://github.com/will/crystal-pg.git - version: 0.22.1 + version: 0.23.1 pool: git: https://github.com/ysbaddaden/pool.git @@ -38,5 +38,5 @@ shards: sqlite3: git: https://github.com/crystal-lang/crystal-sqlite3.git - version: 0.17.0 + version: 0.18.0 @@ -12,10 +12,10 @@ targets: dependencies: pg: github: will/crystal-pg - version: ~> 0.22.1 + version: ~> 0.23.1 sqlite3: github: crystal-lang/crystal-sqlite3 - version: ~> 0.17.0 + version: ~> 0.18.0 kemal: github: kemalcr/kemal version: ~> 0.27.0 @@ -29,6 +29,6 @@ dependencies: github: iv-org/lsquic.cr version: ~> 2.18.1-1 -crystal: 0.35.1 +crystal: 0.36.1 license: AGPLv3 diff --git a/src/invidious.cr b/src/invidious.cr index 713d193e..563a3768 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -84,7 +84,9 @@ LOCALES = { "fa" => load_locale("fa"), "fi" => load_locale("fi"), "fr" => load_locale("fr"), + "he" => load_locale("he"), "hr" => load_locale("hr"), + "id" => load_locale("id"), "is" => load_locale("is"), "it" => load_locale("it"), "ja" => load_locale("ja"), @@ -311,12 +313,12 @@ before_all do |env| env.set "current_page", URI.encode_www_form(current_page) end -Invidious::Routing.get "/", Invidious::Routes::Home -Invidious::Routing.get "/privacy", Invidious::Routes::Privacy -Invidious::Routing.get "/licenses", Invidious::Routes::Licenses +Invidious::Routing.get "/", Invidious::Routes::Misc, :home +Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy +Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses Invidious::Routing.get "/watch", Invidious::Routes::Watch -Invidious::Routing.get "/embed/", Invidious::Routes::Embed::Index -Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed::Show +Invidious::Routing.get "/embed/", Invidious::Routes::Embed, :redirect +Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed, :show Invidious::Routing.get "/view_all_playlists", Invidious::Routes::Playlists, :index Invidious::Routing.get "/create_playlist", Invidious::Routes::Playlists, :new Invidious::Routing.post "/create_playlist", Invidious::Routes::Playlists, :create @@ -335,9 +337,9 @@ Invidious::Routing.get "/search", Invidious::Routes::Search, :search Invidious::Routing.get "/login", Invidious::Routes::Login, :login_page Invidious::Routing.post "/login", Invidious::Routes::Login, :login Invidious::Routing.post "/signout", Invidious::Routes::Login, :signout -Invidious::Routing.get "/preferences", Invidious::Routes::UserPreferences, :show -Invidious::Routing.post "/preferences", Invidious::Routes::UserPreferences, :update -Invidious::Routing.get "/toggle_theme", Invidious::Routes::UserPreferences, :toggle_theme +Invidious::Routing.get "/preferences", Invidious::Routes::PreferencesRoute, :show +Invidious::Routing.post "/preferences", Invidious::Routes::PreferencesRoute, :update +Invidious::Routing.get "/toggle_theme", Invidious::Routes::PreferencesRoute, :toggle_theme # Users @@ -1428,9 +1430,9 @@ get "/feed/playlist/:plid" do |env| node.attributes.each do |attribute| case attribute.name when "url", "href" - full_path = URI.parse(node[attribute.name]).full_path - query_string_opt = full_path.starts_with?("/watch?v=") ? "&#{params}" : "" - node[attribute.name] = "#{HOST_URL}#{full_path}#{query_string_opt}" + request_target = URI.parse(node[attribute.name]).request_target + query_string_opt = request_target.starts_with?("/watch?v=") ? "&#{params}" : "" + node[attribute.name] = "#{HOST_URL}#{request_target}#{query_string_opt}" else nil # Skip end end @@ -1439,7 +1441,7 @@ get "/feed/playlist/:plid" do |env| document = document.to_xml(options: XML::SaveOptions::NO_DECL) document.scan(/<uri>(?<url>[^<]+)<\/uri>/).each do |match| - content = "#{HOST_URL}#{URI.parse(match["url"]).full_path}" + content = "#{HOST_URL}#{URI.parse(match["url"]).request_target}" document = document.gsub(match[0], "<uri>#{content}</uri>") end @@ -1634,7 +1636,7 @@ end get "/attribution_link" do |env| if query = env.params.query["u"]? - url = URI.parse(query).full_path + url = URI.parse(query).request_target else url = "/" end @@ -1978,7 +1980,7 @@ get "/api/v1/captions/:id" do |env| caption = caption[0] end - url = URI.parse("#{caption.baseUrl}&tlang=#{tlang}").full_path + url = URI.parse("#{caption.baseUrl}&tlang=#{tlang}").request_target # Auto-generated captions often have cues that aren't aligned properly with the video, # as well as some other markup that makes it cumbersome, so we try to fix that here @@ -3184,7 +3186,7 @@ get "/api/manifest/dash/id/:id" do |env| end if dashmpd = video.dash_manifest_url - manifest = YT_POOL.client &.get(URI.parse(dashmpd).full_path).body + manifest = YT_POOL.client &.get(URI.parse(dashmpd).request_target).body manifest = manifest.gsub(/<BaseURL>[^<]+<\/BaseURL>/) do |baseurl| url = baseurl.lchop("<BaseURL>") @@ -3192,7 +3194,7 @@ get "/api/manifest/dash/id/:id" do |env| if local uri = URI.parse(url) - url = "#{uri.full_path}host/#{uri.host}/" + url = "#{uri.request_target}host/#{uri.host}/" end "<BaseURL>#{url}</BaseURL>" @@ -3205,7 +3207,7 @@ get "/api/manifest/dash/id/:id" do |env| if local adaptive_fmts.each do |fmt| - fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).full_path) + fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) end end @@ -3403,7 +3405,7 @@ get "/latest_version" do |env| next end - url = URI.parse(url).full_path.not_nil! if local + url = URI.parse(url).request_target.not_nil! if local url = "#{url}&title=#{title}" if title env.redirect url @@ -3515,7 +3517,7 @@ get "/videoplayback" do |env| client = make_client(URI.parse(new_host), region) end - url = "#{location.full_path}&host=#{location.host}#{region ? "®ion=#{region}" : ""}" + url = "#{location.request_target}&host=#{location.host}#{region ? "®ion=#{region}" : ""}" else break end @@ -3555,7 +3557,7 @@ get "/videoplayback" do |env| if location = response.headers["Location"]? location = URI.parse(location) - location = "#{location.full_path}&host=#{location.host}" + location = "#{location.request_target}&host=#{location.host}" if region location += "®ion=#{region}" @@ -3619,7 +3621,7 @@ get "/videoplayback" do |env| if location = response.headers["Location"]? location = URI.parse(location) - location = "#{location.full_path}&host=#{location.host}#{region ? "®ion=#{region}" : ""}" + location = "#{location.request_target}&host=#{location.host}#{region ? "®ion=#{region}" : ""}" env.redirect location break @@ -3859,7 +3861,7 @@ end get "/watch_videos" do |env| response = YT_POOL.client &.get(env.request.resource) if url = response.headers["Location"]? - url = URI.parse(url).full_path + url = URI.parse(url).request_target next env.redirect url end @@ -3874,7 +3876,7 @@ error 404 do |env| response = YT_POOL.client &.get("/#{item}") if response.status_code == 301 - response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).full_path) + response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).request_target) end if response.body.empty? diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr index 0ac99ba5..a8bbf74b 100644 --- a/src/invidious/comments.cr +++ b/src/invidious/comments.cr @@ -294,7 +294,7 @@ def template_youtube_comments(comments, locale, thin_mode) end if !thin_mode - author_thumbnail = "/ggpht#{URI.parse(child["authorThumbnails"][-1]["url"].as_s).full_path}" + author_thumbnail = "/ggpht#{URI.parse(child["authorThumbnails"][-1]["url"].as_s).request_target}" else author_thumbnail = "" end @@ -322,7 +322,7 @@ def template_youtube_comments(comments, locale, thin_mode) html << <<-END_HTML <div class="pure-g"> <div class="pure-u-1 pure-u-md-1-2"> - <img style="width:100%" src="/ggpht#{URI.parse(attachment["url"].as_s).full_path}"> + <img style="width:100%" src="/ggpht#{URI.parse(attachment["url"].as_s).request_target}"> </div> </div> END_HTML @@ -375,7 +375,7 @@ def template_youtube_comments(comments, locale, thin_mode) if child["creatorHeart"]? if !thin_mode - creator_thumbnail = "/ggpht#{URI.parse(child["creatorHeart"]["creatorThumbnail"].as_s).full_path}" + creator_thumbnail = "/ggpht#{URI.parse(child["creatorHeart"]["creatorThumbnail"].as_s).request_target}" else creator_thumbnail = "" end @@ -473,7 +473,7 @@ def replace_links(html) params = HTTP::Params.parse(url.query.not_nil!) anchor["href"] = params["q"]? else - anchor["href"] = url.full_path + anchor["href"] = url.request_target end elsif url.to_s == "#" begin @@ -544,7 +544,7 @@ def content_to_comment_html(content) if url.path == "/redirect" url = HTTP::Params.parse(url.query.not_nil!)["q"] else - url = url.full_path + url = url.request_target end end diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 00087143..944d869b 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -231,7 +231,7 @@ def extract_item(item : JSON::Any, author_fallback : String? = nil, author_id_fa video_id = i["videoId"].as_s title = i["title"].try { |t| t["simpleText"]?.try &.as_s || t["runs"]?.try &.as_a.map(&.["text"].as_s).join("") } || "" - author_info = i["ownerText"]?.try &.["runs"].as_a[0]? + author_info = i["ownerText"]?.try &.["runs"]?.try &.as_a?.try &.[0]? author = author_info.try &.["text"].as_s || author_fallback || "" author_id = author_info.try &.["navigationEndpoint"]?.try &.["browseEndpoint"]["browseId"].as_s || author_id_fallback || "" @@ -322,7 +322,7 @@ def extract_item(item : JSON::Any, author_fallback : String? = nil, author_id_fa video_count = i["videoCount"]?.try &.as_s.to_i || 0 playlist_thumbnail = i["thumbnails"].as_a[0]?.try &.["thumbnails"]?.try &.as_a[0]?.try &.["url"].as_s || "" - author_info = i["shortBylineText"]?.try &.["runs"].as_a[0]? + author_info = i["shortBylineText"]?.try &.["runs"]?.try &.as_a?.try &.[0]? author = author_info.try &.["text"].as_s || author_fallback || "" author_id = author_info.try &.["navigationEndpoint"]?.try &.["browseEndpoint"]["browseId"].as_s || author_id_fallback || "" diff --git a/src/invidious/helpers/proxy.cr b/src/invidious/helpers/proxy.cr index 7a42ef41..3418d887 100644 --- a/src/invidious/helpers/proxy.cr +++ b/src/invidious/helpers/proxy.cr @@ -71,14 +71,14 @@ end class HTTPClient < HTTP::Client def set_proxy(proxy : HTTPProxy) begin - @socket = proxy.open(host: @host, port: @port, tls: @tls, connection_options: proxy_connection_options) + @io = proxy.open(host: @host, port: @port, tls: @tls, connection_options: proxy_connection_options) rescue IO::Error - @socket = nil + @io = nil end end def unset_proxy - @socket = nil + @io = nil end def proxy_connection_options diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 7d94a6e5..2c95a373 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -329,7 +329,7 @@ def get_referer(env, fallback = "/", unroll = true) end end - referer = referer.full_path + referer = referer.request_target referer = "/" + referer.gsub(/[^\/?@&%=\-_.0-9a-zA-Z]/, "").lstrip("/\\") if referer == env.request.path diff --git a/src/invidious/jobs/bypass_captcha_job.cr b/src/invidious/jobs/bypass_captcha_job.cr index 8b1aed5f..4269e123 100644 --- a/src/invidious/jobs/bypass_captcha_job.cr +++ b/src/invidious/jobs/bypass_captcha_job.cr @@ -60,7 +60,7 @@ class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob elsif response.headers["Location"]?.try &.includes?("/sorry/index") location = response.headers["Location"].try { |u| URI.parse(u) } headers = HTTP::Headers{":authority" => location.host.not_nil!} - response = YT_POOL.client &.get(location.full_path, headers) + response = YT_POOL.client &.get(location.request_target, headers) html = XML.parse_html(response.body) form = html.xpath_node(%(//form[@action="index"])).not_nil! diff --git a/src/invidious/routes/embed/show.cr b/src/invidious/routes/embed.cr index 8a655556..5db32788 100644 --- a/src/invidious/routes/embed/show.cr +++ b/src/invidious/routes/embed.cr @@ -1,5 +1,29 @@ -class Invidious::Routes::Embed::Show < Invidious::Routes::BaseRoute - def handle(env) +class Invidious::Routes::Embed < Invidious::Routes::BaseRoute + def redirect(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + + if plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "") + begin + playlist = get_playlist(PG_DB, plid, locale: locale) + offset = env.params.query["index"]?.try &.to_i? || 0 + videos = get_playlist_videos(PG_DB, playlist, offset: offset, locale: locale) + rescue ex + return error_template(500, ex) + end + + url = "/embed/#{videos[0].id}?#{env.params.query}" + + if env.params.query.size > 0 + url += "?#{env.params.query}" + end + else + url = "/" + end + + env.redirect url + end + + def show(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? id = env.params.url["id"] @@ -120,8 +144,8 @@ class Invidious::Routes::Embed::Show < Invidious::Routes::BaseRoute adaptive_fmts = video.adaptive_fmts if params.local - fmt_stream.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).full_path) } - adaptive_fmts.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).full_path) } + fmt_stream.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) } + adaptive_fmts.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) } end video_streams = video.video_streams diff --git a/src/invidious/routes/embed/index.cr b/src/invidious/routes/embed/index.cr deleted file mode 100644 index 32a4966b..00000000 --- a/src/invidious/routes/embed/index.cr +++ /dev/null @@ -1,25 +0,0 @@ -class Invidious::Routes::Embed::Index < Invidious::Routes::BaseRoute - def handle(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - - if plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "") - begin - playlist = get_playlist(PG_DB, plid, locale: locale) - offset = env.params.query["index"]?.try &.to_i? || 0 - videos = get_playlist_videos(PG_DB, playlist, offset: offset, locale: locale) - rescue ex - return error_template(500, ex) - end - - url = "/embed/#{videos[0].id}?#{env.params.query}" - - if env.params.query.size > 0 - url += "?#{env.params.query}" - end - else - url = "/" - end - - env.redirect url - end -end diff --git a/src/invidious/routes/licenses.cr b/src/invidious/routes/licenses.cr deleted file mode 100644 index 38fde7bb..00000000 --- a/src/invidious/routes/licenses.cr +++ /dev/null @@ -1,6 +0,0 @@ -class Invidious::Routes::Licenses < Invidious::Routes::BaseRoute - def handle(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - rendered "licenses" - end -end diff --git a/src/invidious/routes/login.cr b/src/invidious/routes/login.cr index 662fdf13..ffe5f568 100644 --- a/src/invidious/routes/login.cr +++ b/src/invidious/routes/login.cr @@ -255,7 +255,7 @@ class Invidious::Routes::Login < Invidious::Routes::BaseRoute traceback << "Unhandled dialog /b/0/SmsAuthInterstitial." end - login = client.get(location.full_path, headers) + login = client.get(location.request_target, headers) headers = login.cookies.add_request_headers(headers) location = login.headers["Location"]?.try { |u| URI.parse(u) } diff --git a/src/invidious/routes/home.cr b/src/invidious/routes/misc.cr index 486a7344..bc009633 100644 --- a/src/invidious/routes/home.cr +++ b/src/invidious/routes/misc.cr @@ -1,5 +1,5 @@ -class Invidious::Routes::Home < Invidious::Routes::BaseRoute - def handle(env) +class Invidious::Routes::Misc < Invidious::Routes::BaseRoute + def home(env) preferences = env.get("preferences").as(Preferences) locale = LOCALES[preferences.locale]? user = env.get? "user" @@ -25,4 +25,14 @@ class Invidious::Routes::Home < Invidious::Routes::BaseRoute templated "empty" end end + + def privacy(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + templated "privacy" + end + + def licenses(env) + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + rendered "licenses" + end end diff --git a/src/invidious/routes/user_preferences.cr b/src/invidious/routes/preferences.cr index a689a2a2..4901d22b 100644 --- a/src/invidious/routes/user_preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -1,4 +1,4 @@ -class Invidious::Routes::UserPreferences < Invidious::Routes::BaseRoute +class Invidious::Routes::PreferencesRoute < Invidious::Routes::BaseRoute def show(env) locale = LOCALES[env.get("preferences").as(Preferences).locale]? diff --git a/src/invidious/routes/privacy.cr b/src/invidious/routes/privacy.cr deleted file mode 100644 index 4565c94c..00000000 --- a/src/invidious/routes/privacy.cr +++ /dev/null @@ -1,6 +0,0 @@ -class Invidious::Routes::Privacy < Invidious::Routes::BaseRoute - def handle(env) - locale = LOCALES[env.get("preferences").as(Preferences).locale]? - templated "privacy" - end -end diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index 65604a88..8169e1ed 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -126,8 +126,8 @@ class Invidious::Routes::Watch < Invidious::Routes::BaseRoute adaptive_fmts = video.adaptive_fmts if params.local - fmt_stream.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).full_path) } - adaptive_fmts.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).full_path) } + fmt_stream.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) } + adaptive_fmts.each { |fmt| fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target) } end video_streams = video.video_streams diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index caa0ad0e..061d7eec 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -5,7 +5,7 @@ <% if channel.banner %> <div class="h-box"> - <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).full_path %>"> + <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).request_target %>"> </div> <div class="h-box"> @@ -16,7 +16,7 @@ <div class="pure-g h-box"> <div class="pure-u-2-3"> <div class="channel-profile"> - <img src="/ggpht<%= URI.parse(channel.author_thumbnail).full_path %>"> + <img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>"> <span><%= channel.author %></span> </div> </div> diff --git a/src/invidious/views/community.ecr b/src/invidious/views/community.ecr index 69724390..3c4eaabb 100644 --- a/src/invidious/views/community.ecr +++ b/src/invidious/views/community.ecr @@ -4,7 +4,7 @@ <% if channel.banner %> <div class="h-box"> - <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).full_path %>"> + <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).request_target %>"> </div> <div class="h-box"> @@ -15,7 +15,7 @@ <div class="pure-g h-box"> <div class="pure-u-2-3"> <div class="channel-profile"> - <img src="/ggpht<%= URI.parse(channel.author_thumbnail).full_path %>"> + <img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>"> <span><%= channel.author %></span> </div> </div> diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index e4a60697..ea7d356c 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -5,7 +5,7 @@ <a style="width:100%" href="/channel/<%= item.ucid %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <center> - <img style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).full_path %>"/> + <img style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/> </center> <% end %> <p><%= item.author %></p> @@ -15,7 +15,7 @@ <h5><%= item.description_html %></h5> <% when SearchPlaylist, InvidiousPlaylist %> <% if item.id.starts_with? "RD" %> - <% url = "/mix?list=#{item.id}&continuation=#{URI.parse(item.thumbnail || "/vi/-----------").full_path.split("/")[2]}" %> + <% url = "/mix?list=#{item.id}&continuation=#{URI.parse(item.thumbnail || "/vi/-----------").request_target.split("/")[2]}" %> <% else %> <% url = "/playlist?list=#{item.id}" %> <% end %> @@ -23,7 +23,7 @@ <a style="width:100%" href="<%= url %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> - <img class="thumbnail" src="<%= URI.parse(item.thumbnail || "/").full_path %>"/> + <img class="thumbnail" src="<%= URI.parse(item.thumbnail || "/").request_target %>"/> <p class="length"><%= number_with_separator(item.video_count) %> videos</p> </div> <% end %> diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 625c6fee..a898a41f 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -4,7 +4,7 @@ <% if params.video_loop %>loop<% end %> <% if params.controls %>controls<% end %>> <% if (hlsvp = video.hls_manifest_url) && !CONFIG.disabled?("livestreams") %> - <source src="<%= URI.parse(hlsvp).full_path %><% if params.local %>?local=true<% end %>" type="application/x-mpegURL" label="livestream"> + <source src="<%= URI.parse(hlsvp).request_target %><% if params.local %>?local=true<% end %>" type="application/x-mpegURL" label="livestream"> <% else %> <% if params.listen %> <% audio_streams.each_with_index do |fmt, i| %> diff --git a/src/invidious/views/playlists.ecr b/src/invidious/views/playlists.ecr index a77d106d..44bdb94d 100644 --- a/src/invidious/views/playlists.ecr +++ b/src/invidious/views/playlists.ecr @@ -4,7 +4,7 @@ <% if channel.banner %> <div class="h-box"> - <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).full_path %>"> + <img style="width:100%" src="/ggpht<%= URI.parse(channel.banner.not_nil!.gsub("=w1060-", "=w1280-")).request_target %>"> </div> <div class="h-box"> @@ -15,7 +15,7 @@ <div class="pure-g h-box"> <div class="pure-u-2-3"> <div class="channel-profile"> - <img src="/ggpht<%= URI.parse(channel.author_thumbnail).full_path %>"> + <img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>"> <span><%= channel.author %></span> </div> </div> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 786a88b6..924914a5 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -22,6 +22,7 @@ <meta name="twitter:player" content="<%= HOST_URL %>/embed/<%= video.id %>"> <meta name="twitter:player:width" content="1280"> <meta name="twitter:player:height" content="720"> +<link rel="alternate" href="https://www.youtube.com/watch?v=<%= video.id %>"> <%= rendered "components/player_sources" %> <title><%= HTML.escape(video.title) %> - Invidious</title> <% end %> @@ -203,7 +204,7 @@ <a href="/channel/<%= video.ucid %>" style="display:block;width:fit-content;width:-moz-fit-content"> <div class="channel-profile"> <% if !video.author_thumbnail.empty? %> - <img src="/ggpht<%= URI.parse(video.author_thumbnail).full_path %>"> + <img src="/ggpht<%= URI.parse(video.author_thumbnail).request_target %>"> <% end %> <span id="channel-name"><%= video.author %></span> </div> |
